const initEventHandlers = require('./control/control');
const getLayer = require('./layer/layer');
const fly = require('./camera/camera').fly;
const glMatrix = require('gl-matrix');
const mat4 = glMatrix.mat4;
const Scene = require('./scene/scene');

module.exports = class MinemapGlobe {
    /* 
     * option{
     *  container,
     *  maskUrl,
     *  backgroundUrl,
     *  mapTileService
     * }
     */
    constructor(option) {
        // get option config
        this.container = document.getElementById(option.container);

        // init container canvas
        const canvas = document.createElement('canvas');
        canvas.style.position = 'absolute';
        canvas.style.display = 'block';
        this.canvas = canvas;
        this.container.appendChild(canvas);

        //init layers arr
        this.layers = [];

        // init gl
        this.gl = canvas.getContext('webgl'); // to be fixed
        const gl = this.gl;
        if(!this.gl) {
            console.log('Failed to get the rendering context for WebGL!');
            return;
        }

        // init globe info
        this.currentdis = 4.18589155;
        this.zoom = 3;
        this.lngLat = [90.0, 0.0];   
        this.radius = 2.0;     
        this.config = {};
        this.config.currentAngle = [0.0, 0.0];
        this.config.viewMatrix = mat4.create();
        this.config.projMatrix = mat4.create();
        this.config.modelMatrix = mat4.create();
        this.config.normalMatrix = mat4.create();

        this.width = this.container.offsetWidth;
        this.height = this.container.offsetHeight;
        this.canvas.height = this.height;
        this.canvas.width = this.width;
        this.canvas.style.height = this.height + 'px';
        this.canvas.style.width = this.width + 'px';

        this.setPerspective();
        this.setView();
        this.setModelNormal();

        this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);    
        // gl.drawingBufferWidth = this.canvas.width;
        // gl.drawingBufferHeight = this.canvas.height;

        gl.disable(gl.CULL_FACE); //启用裁剪提高性能，绘制地图时启用
        gl.frontFace(gl.CW);//指定顺时针方向为正面
        gl.cullFace(gl.BACK); //裁剪掉背面

        this.scene = new Scene(this.radius + 0.01, this.width, this.height, this.config.viewMatrix, this.config.projMatrix);

        // init event handlers
        initEventHandlers(this);
        // this.resize();
        
        this.haloLayer = getLayer({
            gl: gl,
            globe: this,
            radius: this.radius + 0.011,
            id: 'color',
            type: 'color',
        });

        // add star background layer
        if (option.backgroundUrl) {
            const starBackgroundLayer = getLayer({
                id: 'starBackground',
                gl: gl,
                globe: this,
                type: 'const-texture',
                radius: this.radius * 12.5,
                textureUrl: option.backgroundUrl
            });
            if (starBackgroundLayer) {
                this.layers.push(starBackgroundLayer);
            }
        }

        // add tile layer
        const tileBaseLayer = getLayer({
            id: 'tileBase',
            gl: gl,
            globe: this,
            type: 'tile-texture',
            radius: this.radius + 0.01,
            isTileRender: false,
            // textureUrl: '//mt1.google.cn/vt/lyrs=s,r&hl=zh-CN&gl=cn&x={x}&y={reverseY}&z={z}',
            textureUrl: option.mapTileService ? option.mapTileService : 'tecent'
        });
        if (tileBaseLayer) {
            this.layers.push(tileBaseLayer);
        }

        const tileLayer = getLayer({
            id: 'tile',
            gl: gl,
            globe: this,
            type: 'tile-texture',
            radius: this.radius + 0.011,
            isTileRender: true,
            // textureUrl: '//mt1.google.cn/vt/lyrs=s,r&hl=zh-CN&gl=cn&x={x}&y={reverseY}&z={z}',
            textureUrl: option.mapTileService ? option.mapTileService : 'tecent'
        });
        if (tileLayer) {
            this.layers.push(tileLayer);
        }

        // add base layer
        if (option.maskUrl) {
            const maskLayer = getLayer({
                id: 'base',
                gl: gl,
                globe: this,
                type: 'const-texture',
                radius: this.radius + 0.015,
                textureUrl: option.maskUrl
            });
            if (maskLayer) {
                this.layers.push(maskLayer);
            }
        };

        this.animation();
    }

    setPerspective() {
        if (!this.config.projMatrix) {
            this.config.projMatrix = mat4.create();
        }
        mat4.perspective(this.config.projMatrix, Math.PI / 4, this.width / this.height, 0.1, 100);
    }

    setView() {
        if (!this.config.viewMatrix) {
            this.config.viewMatrix = mat4.create();
        }
        mat4.lookAt(this.config.viewMatrix, [0, 0, this.currentdis + 2.1], [0, 0, 0], [0, 1, 0]);
        mat4.rotateX(this.config.viewMatrix, this.config.viewMatrix, this.config.currentAngle[0] / 180 * Math.PI);
        mat4.rotateY(this.config.viewMatrix, this.config.viewMatrix, this.config.currentAngle[1] / 180 * Math.PI);
    }

    setModelNormal() {
        if (!this.config.modelMatrix) {
            this.config.modelMatrix = mat4.create();
        }
        if (!this.config.normalMatrix) {
            this.config.normalMatrix = mat4.create();
        }
        mat4.identity(this.config.modelMatrix);
    }

    getZoom() {
        return this.zoom;
    }

    setZoom(n) {
        if (typeof (n) === 'number' && n >= 1 && n <= 9) {
            this.zoom = n;
            this.zoomToDis()
        }
    }

    // disToZoom() {
    //     this.zoom = -1.044 * Math.log(this.currentdis) + 4.7423;
    //     this.setViewPerspective();
    // }

    // zoomToDis() {
    //     this.currentdis = Math.exp((this.zoom - 4.7423) / -1.044);
    //     this.setViewPerspective();
    // }

    disToZoom() {
        this.zoom = Math.log(this.currentdis / 36.369) / -0.721;
    }

    zoomToDis() {
        this.currentdis = 36.369 * Math.exp(-0.721 * this.zoom);
        this.setView();
    }

    getLngLat() {
        return this.lngLat;
    }

    setLngLat(lngLat) {
        this.config.currentAngle[1] = 90 - lngLat[0];
        this.config.currentAngle[0] = lngLat[1];

        this.dealLngLat();
    }

    dealLngLat() {
        let lngLat = this.lngLat;
        lngLat[0] = 90 - this.config.currentAngle[1] % 360;
        if (lngLat[0] > 180) {
            lngLat[0] = -180 + (lngLat[0] - 180);
        }
        else if (lngLat[0] < -180) {
            lngLat[0] = 180 + (lngLat[0] + 180);
        }
        lngLat[1] = 0 + this.config.currentAngle[0] % 360;
        // this.setModelNormal();

        
        mat4.lookAt(this.config.viewMatrix, [0, 0, this.currentdis + 2.1], [0, 0, 0], [0, 1, 0]);
        mat4.rotateX(this.config.viewMatrix, this.config.viewMatrix, this.config.currentAngle[0] / 180 * Math.PI);
        mat4.rotateY(this.config.viewMatrix, this.config.viewMatrix, this.config.currentAngle[1] / 180 * Math.PI);
    }

    addLayer(option) {
        option.gl = this.gl;
        option.globe = this;
        option.radius = this.radius + 0.011;
        option.scene = this.scene;
        const layer = getLayer(option);
        if (layer) {
            this.layers.push(layer);
        }
    }

    updateSence() {
        const gl = this.gl;

        gl.enable(gl.DEPTH_TEST);
        gl.clearColor(0.0, 0.0, 0.0, 1.0);

        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        for(let i = 0; i < this.layers.length; i++) {
            this.layers[i].render(this.config);
        }

        this.haloLayer.render(this.config);
    }

    animation() {
        this.updateSence();
        requestAnimationFrame(this.animation.bind(this))
    }

    fly(from, to, time, moveType, callback) {
        fly(this, from, to, time, moveType, callback);
    }
}
